<?php
namespace app\common\dao;

use think\Collection;
use think\db\exception\DbException;
use think\facade\Db;
use think\Model;


abstract class BaseDao
{

    /**
     * @return BaseModel
     */
    abstract protected function getModel(): string;


    /**
     * @return string
     */
    public function getPk()
    {
        return ($this->getModel())::tablePk();
    }

    /**
     * @param int $id
     * @return bool
     */
    public function exists(int $id)
    {
        return $this->fieldExists($this->getPk(), $id);
    }


    /**
     * @param $field
     * @param $value
     * @param int|null $except
     * @return bool
     */
    public function fieldExists($field, $value, ?int $except = null): bool
    {
        $query = ($this->getModel())::getDB()->where($field, $value);
        if (!is_null($except)) $query->where($this->getPk(), '<>', $except);
        return $query->count() > 0;
    }

    /**
     * @param array $data
     * @return self|Model
     */
    public function create(array $data)
    {
        $data = $this->formateModelAttributes($data);
        if(isset($data[$this->getPk()])){
            unset($data[$this->getPk()]);
        }
        return $this->getModel()::create($data);
    }

    /**
     * @param array $data
     * @return Collection|Model|null
     */
    public function find(array $data)
    {
        return ($this->getModel())::create($data);
    }

    /**
     * 插入数据并返回id
     */
    public function insertGetId_(array $data){
        return ($this->getModel())::insertGetId($data);
    }

    /**
     * 插入数据
     */
    public function insert(array $data){
        return ($this->getModel())::insert($data);
    }

    /**
     * @param int $id
     * @param array $data
     * @return int
     * @throws DbException
     */
    public function update(int $id, array $data)
    {
        if($this->exists($id)){
            return ($this->getModel())::getDB()->where($this->getPk(), $id)->find()->save($data);
        }
    }

    /**
     * 根据条件修改
     */
    public function updateByWhere(array $where, array $update)
    {
        $count = 0;
        $obj = ($this->getModel())::getDB()->where($where)->select()->each(function($item) use ($update,&$count){
            $item->save($update);
            $count++;
        });
        return  $count;
    }

    /**
     * @param array $ids
     * @param array $data
     * @return int
     * @throws DbException
     */
    public function updates(array $ids, array $data)
    {
        return ($this->getModel())::getDB()->whereIn($this->getPk(), $ids)->select()->each(function($item) use ($data){
            $item->save($data);
        });
    }


    /**
     * @param int $id
     * @return int
     * @throws DbException
     */
    public function delete(int $id)
    {
        return ($this->getModel())::getDB()->where($this->getPk(), $id)->delete();
    }

    /**
     * 根据条件删除
     * @param array $where
     * @return mixed
     */
    public function deleteByWhere(array $where)
    {
        return ($this->getModel())::getDB()->where($where)->delete();
    }


    /**
     * @param int $id
     * @return array|Model|null
     */
    public function get($id,$field='*')
    {
        return ($this->getModel())::getInstance()->field($field)->find($id);
    }

    /**
     * @param array $where
     * @param string $field
     * @return array|Model|null
     */
    public function getWhere(array $where, string $field = '*', array $with = [])
    {
        return ($this->getModel())::getInstance()->where($where)->when($with, function ($query) use ($with) {
            $query->with($with);
        })->field($field)->find();
    }

    /**
     * @param array $where
     * @param string $field
     * @return Collection
     */
    public function selectWhere(array $where, string $field = '*',array $with = [])
    {
        return ($this->getModel())::getInstance()->where($where)->when($with, function ($query) use ($with) {
            $query->with($with);
        })->field($field)->select();
    }

    /**
     * @param int $id
     * @param array $with
     * @return array|Model|null
     */
    public function getWith(int $id, $with = [])
    {
        return ($this->getModel())::getInstance()->with($with)->find($id);
    }

    public function getWithSelect($with = [],$where = [])
    {
        return ($this->getModel())::getInstance()->with($with)->where($where)->select();
    }


    /**
     * @param array $data
     * @return int
     */
    public function insertAll(array $data)
    {
        return ($this->getModel())::getDB()->insertAll($data);
    }

    /**
     * TODO 通过条件判断是否存在
     * @param array $where
     */
    public function getWhereCount(array $where)
    {
        return ($this->getModel()::getDB())->where($where)->count();
    }

    public function existsWhere($where)
    {
        return ($this->getModel())::getDB()->where($where)->count() > 0;
    }

    /**
     * TODO 查询,如果不存在就创建
     * @param array $where
     * @return array|Model|null
     */
    public function findOrCreate(array $where)
    {
       $res = ($this->getModel()::getDB())->where($where)->find();
       if(!$res)$res = $this->getModel()::create($where);
       return $res;
    }

    /**
     * TODO 搜索
     * @param $where
     * @return BaseModel
     */
    public function getSearch(array $where)
    {
        foreach ($where as $key => $item) {
            if ($item !== '') {
                $keyArray[] = $key;
                $whereArr[$key] = $item;
            }
        }
        if(empty($keyArray)){
            return ($this->getModel())::getDB();
        }else{
            return ($this->getModel())::withSearch($keyArray, $whereArr);
        }
    }

    /**
     * TODO 自增
     * @param array $id
     * @param string $field
     * @param int $num
     * @return mixed
     */
    public function incField(int $id, string $field , $num = 1)
    {
        return ($this->getModel()::getDB())->where($this->getPk(),$id)->inc($field,$num)->update();
    }

    /**
     * TODO 自减
     * @param array $id
     * @param string $field
     * @param int $num
     * @return mixed
     */
    public function decField(int $id, string $field , $num = 1)
    {
        return ($this->getModel()::getDB())
            ->where($this->getPk(),$id)
            ->where($field, '>=' ,$num)
            ->dec($field,$num)->update();
    }

    /**
     * 根据条件查找数据
     */
    public function findByWhere($where,$field = '*')
    {
        return ($this->getModel()::getDB())->where($where)->field($field)->find();
    }
    /**
     * @desc: 基于条件获取统计数量
     * @author: xiaohp
     * @param {*} $where
     */
    public function getStatisticsCountByWhere($where = []){
        return ($this->getModel()::getDB())->where($where)->count();
    }
    /**
     * @desc: 获取Db 查询类实体
     * @author: xiaohp
     */
    public function getDbQueryEntity(){
        return Db::connect()->name($this->getModel()::tableName());
    }
    /**
     * @desc: 格式化模型对象属性
     * @author: xiaohp
     * @param {*} $attributes
     */
    public function formateModelAttributes($attributes = []){
        $fields = ($this->getModel()::getDB())->getFields();
        $fields_keys = array_keys($fields);
        $data = [];
        if(!empty($attributes)){
            foreach($attributes as $key => $value){
                if(in_array($key,$fields_keys)){
                    $data[$key] = $value;
                }
            }
        }
        return $data;
    }
}